
/**
 * AppleIIGo
 * IOU (Input Output Unit) class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include "IOU.h"

#include <stdio.h>
void onIo(bool isWrite, int address, int value) {
	return;
	bool isRemoveDebugging = 1;
	if (address >= 0xc100)
		return;
	if (address == 0xc010)
		return;
	if (address == 0xc028)
		return;
	if (address == 0xc030)
		return;
//	if ((address & 0xc0f0) == 0xc080)
//		return;
	if ((address == 0xc000) && !isWrite)
		return;
	printf("%s %04x:%02x\n", isWrite ? "W" : "R", address, value);
}

IOU::IOU(Motherboard * motherboard, UINT32 iouType) :
	Device(motherboard, "IOU") {
	isIIGSIOU = (iouType == IOUTYPE_IIGS);
	isIIcIOU = (iouType == IOUTYPE_IIC);
	
	isIIjplusIOU = (iouType == IOUTYPE_IIJPLUS);
	
	switch (iouType) {
		case IOUTYPE_IIGS:
		case IOUTYPE_IIC:
		case IOUTYPE_IIEENH:
			isIIeEnhancedIOU = true;
			isIIeIOU = true;
			break;
		case IOUTYPE_IIE:
			isIIeEnhancedIOU = false;
			isIIeIOU = true;
			break;
		default:
			isIIeEnhancedIOU = false;
			isIIeIOU = false;
			break;
	}
}

void IOU::onDevicesConnected() {
	cpu = motherboard->getCpu();
	mmu = motherboard->getMmu();
	memory = motherboard->getMemory();
	mig = motherboard->getMig();
	timingGenerator = motherboard->getTimingGenerator();
	videoRenderer = motherboard->getVideoRenderer();
	speaker = motherboard->getSpeaker();
	keyboard = motherboard->getKeyboard();
	paddles = motherboard->getPaddles();
	mouse = motherboard->getMouse();
	adb = motherboard->getAdb();
	doc = motherboard->getDoc();
	rtc = motherboard->getRtc();
	sccA = motherboard->getSccA();
	sccB = motherboard->getSccB();
	for (int slotIndex = 0; slotIndex < SLOT_END; slotIndex++)
		slot[slotIndex] = motherboard->getSlotDevice(slotIndex);
	auxSlot = motherboard->getAuxSlotDevice();
	chip = motherboard->getChipDevice();
	
	mappedSlot[0] = isIIeIOU ? slot[SLOT_INTERNAL] : slot[SLOT_EXTERNAL];
}

void IOU::onResetAsserted(bool isRestart) {
	if (isRestart)
		ioSlotSel = 0;
	
	setSlot3External(!isIIeIOU);
	setSlotRegister(isIIcIOU ? 0x00 : 0xff);
	
	isIIcIOUEN = false;
}

void IOU::writeIo(UINT32 address, UINT8 value) {
	onIo(true, address, value);
	
	switch (address & 0xfff) {
		case 0x00: case 0x01:
			if (isIIeIOU) {
				videoRenderer->set80Store(address & 0x1);
				mmu->set80Store(address & 0x1);
			}
			break;
		case 0x02: case 0x03:
			if (isIIeIOU)
				mmu->setAuxRead(address & 0x1);
			break;
		case 0x04: case 0x05:
			if (isIIeIOU)
				mmu->setAuxWrite(address & 0x1);
			break;
		case 0x06: case 0x07:
			if (isIIeIOU && !isIIcIOU)
				mmu->setRomInternal(address & 0x1);
			break;
		case 0x08: case 0x09:
			if (isIIeIOU)
				mmu->setAuxZeropage(address & 0x1);
			break;
		case 0x0a: case 0x0b:
			if (isIIeIOU && !isIIcIOU)
				setSlot3External(address & 0x1);
			break;
		case 0x0c: case 0x0d:
			if (isIIeIOU)
				videoRenderer->set80Col(address & 0x1);
			break;
		case 0x0e: case 0x0f:
			if (isIIeIOU)
				videoRenderer->setAltChar(address & 0x1);
			break;
			
		case 0x10: case 0x11: case 0x12: case 0x13:
		case 0x14: case 0x15: case 0x16: case 0x17:
		case 0x18: case 0x19: case 0x1a: case 0x1b:
		case 0x1c: case 0x1d: case 0x1e: case 0x1f:
			keyboard->clearKeyLatch();
			break;
			
		case 0x20:
			speaker->toggleCassetteOutput();
			break;
		case 0x21:
			if (isIIGSIOU)
				videoRenderer->setMonochromeRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x22:
			if (isIIGSIOU)
				videoRenderer->setScreenColorRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x23:
			if (isIIGSIOU)
				timingGenerator->setVgcInterruptRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x24:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
		case 0x25:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
		case 0x26:
			if (isIIGSIOU)
				adb->setDataRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x27:
			if (isIIGSIOU)
				adb->setStatusRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x28:
			if (isIIcIOU || isIIGSIOU)
				mmu->setRomBank2(!mmu->isRomBank2());
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x29:
			if (isIIGSIOU) {
				videoRenderer->setNewVideoRegister(value);
				mmu->setShadowBankLatchEnabled(
					!getBit(value, NEWVIDEO_BANKLATCHDISABLED));
			} else
				speaker->toggleCassetteOutput();
			break;
		case 0x2a:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
		case 0x2b:
			if (isIIGSIOU) {
				videoRenderer->setLanguageRegister(value);
				timingGenerator->setPalTiming(getBit(value, LANGUAGE_PAL));
			} else
				speaker->toggleCassetteOutput();
			break;
		case 0x2c:
			if (isIIGSIOU)
				videoRenderer->setCharacterSetReadAddress(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2d:
			if (isIIGSIOU)
				setSlotRegister(value);
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2e:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
		case 0x2f:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
			
		case 0x30:
			speaker->toggleSpeakerOutput();
			break;
		case 0x31:
			if (isIIGSIOU)
				slot[SLOT_INTERNAL + 6]->writeIo(address, value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x32:
			if (isIIGSIOU)
				timingGenerator->clearVgcInterruptRegister(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x33:
			if (isIIGSIOU)
				rtc->sendData(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x34:
			if (isIIGSIOU) {
				videoRenderer->setBorderColorRegister(value);
				rtc->setControlRegister(value);
			} else
				speaker->toggleSpeakerOutput();
			break;
		case 0x35:
			if (isIIGSIOU)
				mmu->setShadowRegister(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x36:
			if (isIIGSIOU) {
				mmu->setShadowAllBanks(getBit(value, SPEED_SHADOWALLBANKS));
				timingGenerator->setSpeedRegister(value);
			} else
				speaker->toggleSpeakerOutput();
			break;
		case 0x37:
			if (isIIGSIOU)
				mmu->setDmaBank(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x38:
			if (isIIGSIOU)
				sccB->setCommand(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x39:
			if (isIIGSIOU)
				sccA->setCommand(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3a:
			if (isIIGSIOU)
				sccB->sendData(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3b:
			if (isIIGSIOU)
				sccA->sendData(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3c:
			if (isIIGSIOU)
				doc->setControlRegister(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3d:
			if (isIIGSIOU)
				doc->sendData(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3e:
			if (isIIGSIOU)
				doc->setAddressLow(value);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3f:
			if (isIIGSIOU)
				doc->setAddressHigh(value);
			else
				speaker->toggleSpeakerOutput();
			break;
			
		case 0x40:
			break;
		case 0x41:
			if (isIIGSIOU) {
				mouse->setInterruptRegister(value);
				timingGenerator->enableVblInterrupt(
					getBit(value, INTERRUPT_VBLINTENABLED));
				timingGenerator->enableQuarterSecondInterrupt(
					getBit(value, INTERRUPT_QUARTERSECINTENABLED));
			}
			break;
		case 0x42: case 0x43: case 0x44: case 0x45: case 0x46:
			break;
		case 0x47:
			if (isIIGSIOU) {
				if (!getBit(value, INTERRUPTCLEAR_CLEARVBLINT))
					timingGenerator->clearVblInterrupt();
				if (!getBit(value, INTERRUPTCLEAR_CLEARQUARTERSECINT))
					timingGenerator->clearQuarterSecondInterrupt();
			}
			break;
		case 0x48:
			if (isIIGSIOU)
				mouse->clearXYInterruptRegister();
			break;
		case 0x49:
		case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
			break;
			
		case 0x50: case 0x51:
			videoRenderer->setText(address & 0x1);
			break;
		case 0x52: case 0x53:
			videoRenderer->setMixed(address & 0x1);
			break;
		case 0x54: case 0x55:
			videoRenderer->setPage2(address & 0x1);
			if (isIIeIOU)
				mmu->setPage2(address & 0x1);
			break;
		case 0x56: case 0x57:
			videoRenderer->setHires(address & 0x1);
			if (isIIeIOU)
				mmu->setHires(address & 0x1);
			break;
		case 0x58: case 0x59:
			if (isIIcIOUEN)
				mouse->enableXYInterrupts(address & 0x1);
			else
				paddles->setAnnunciator(0, address & 0x1);
			if (isAn0ConnectedToVideoterm)
				videoRenderer->setVideoterm(address & 0x1);
			chip->writeIo(address, value);
			break;
		case 0x5a: case 0x5b:
			if (isIIcIOUEN)
				timingGenerator->enableVblInterrupt(address & 0x1);
			else
				paddles->setAnnunciator(1, address & 0x1);
			chip->writeIo(address, value);
			break;
		case 0x5c: case 0x5d:
			if (isIIcIOUEN)
				mouse->setXInterruptOnFallingEdge(address & 0x1);
			else {
				if (isIIjplusIOU)
					videoRenderer->setAltChar(address & 0x1);
				paddles->setAnnunciator(2, address & 0x1);
			}
			chip->writeIo(address, value);
			break;
		case 0x5e: case 0x5f:
			if (isIIcIOUEN)
				mouse->setYInterruptOnFallingEdge(address & 0x1);
			else {
				if (isIIeEnhancedIOU)
					videoRenderer->setDHires(!(address & 0x1));
				paddles->setAnnunciator(3, address & 0x1);
			}
			chip->writeIo(address, value);
			break;
			
		case 0x60: case 0x61: case 0x62: case 0x63:
		case 0x64: case 0x65: case 0x66: case 0x67:
			break;
		case 0x68:
			if (isIIGSIOU) {
				mmu->setStateRegister(value);
				videoRenderer->setPage2(getBit(value, STATE_PAGE2));
			}
			break;
		case 0x69: case 0x6a: case 0x6b: case 0x6c:
		case 0x6d: case 0x6e: case 0x6f:
			break;
			
		case 0x78: case 0x79: case 0x7a: case 0x7b:
		case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			if (isIIcIOU)
				isIIcIOUEN = address & 0x1;
		case 0x70: case 0x71: case 0x72: case 0x73:
		case 0x74: case 0x75: case 0x76: case 0x77:
			paddles->resetTimer();
			if (isIIcIOU)
				timingGenerator->clearVblInterrupt();
			auxSlot->writeIo(address, value);
			chip->writeIo(address, value);
			break;
		case 0x80: case 0x81: case 0x82: case 0x83:
		case 0x84: case 0x85: case 0x86: case 0x87:
		case 0x88: case 0x89: case 0x8a: case 0x8b:
		case 0x8c: case 0x8d: case 0x8e: case 0x8f:
			mappedSlot[0]->writeIo(address, value);
			break;
		case 0x90: case 0x91: case 0x92: case 0x93:
		case 0x94: case 0x95: case 0x96: case 0x97:
		case 0x98: case 0x99: case 0x9a: case 0x9b:
		case 0x9c: case 0x9d: case 0x9e: case 0x9f:
			mappedSlot[1]->writeIo(address, value);
			break;
		case 0xa0: case 0xa1: case 0xa2: case 0xa3:
		case 0xa4: case 0xa5: case 0xa6: case 0xa7:
		case 0xa8: case 0xa9: case 0xaa: case 0xab:
		case 0xac: case 0xad: case 0xae: case 0xaf:
			mappedSlot[2]->writeIo(address, value);
			break;
		case 0xb0: case 0xb1: case 0xb2: case 0xb3:
		case 0xb4: case 0xb5: case 0xb6: case 0xb7:
		case 0xb8: case 0xb9: case 0xba: case 0xbb:
		case 0xbc: case 0xbd: case 0xbe: case 0xbf:
			slot[3]->writeIo(address, value);
			break;
		case 0xc0: case 0xc1: case 0xc2: case 0xc3:
		case 0xc4: case 0xc5: case 0xc6: case 0xc7:
		case 0xc8: case 0xc9: case 0xca: case 0xcb:
		case 0xcc: case 0xcd: case 0xce: case 0xcf:
			mappedSlot[4]->writeIo(address, value);
			break;
		case 0xd0: case 0xd1: case 0xd2: case 0xd3:
		case 0xd4: case 0xd5: case 0xd6: case 0xd7:
		case 0xd8: case 0xd9: case 0xda: case 0xdb:
		case 0xdc: case 0xdd: case 0xde: case 0xdf:
			mappedSlot[5]->writeIo(address, value);
			break;
		case 0xe0: case 0xe1: case 0xe2: case 0xe3:
		case 0xe4: case 0xe5: case 0xe6: case 0xe7:
		case 0xe8: case 0xe9: case 0xea: case 0xeb:
		case 0xec: case 0xed: case 0xee: case 0xef:
			mappedSlot[6]->writeIo(address, value);
			break;
		case 0xf0: case 0xf1: case 0xf2: case 0xf3:
		case 0xf4: case 0xf5: case 0xf6: case 0xf7:
		case 0xf8: case 0xf9: case 0xfa: case 0xfb:
		case 0xfc: case 0xfd: case 0xfe: case 0xff:
			mappedSlot[7]->writeIo(address, value);
			break;
		default:
			if (address & 0x800) {
				if (isIIcIOU)
					mig->writeIo(address, value);
				else if ((address & 0xfff) == 0xfff)
					ioSlotSel = 0;
				else
					mappedSlot[ioSlotSel]->writeMemory(address, value);
			} else {
				ioSlotSel = (address >> 8) & 0x7;
				mappedSlot[ioSlotSel]->writeMemory(address, value);
			}
	}
}

UINT8 IOU::readIo(UINT32 address) {
	onIo(false, address, 0);
	switch (address & 0xfff) {
		case 0x00: case 0x01: case 0x02: case 0x03:
		case 0x04: case 0x05: case 0x06: case 0x07:
		case 0x08: case 0x09: case 0x0a: case 0x0b:
		case 0x0c: case 0x0d: case 0x0e: case 0x0f:
			return keyboard->getKeyLatch();
			
		case 0x10:
			keyboard->clearKeyLatch();
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									keyboard->isAnyKeyDown());
			break;
		case 0x11:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isLanguageCardBank2());
			break;
		case 0x12:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isLanguageCardReadEnabled());
			break;
		case 0x13:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isAuxRead());
			break;
		case 0x14:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isAuxWrite());
			break;
		case 0x15:
			if (isIIcIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mouse->clearXInterrupt());
			else if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isRomInternal());
			break;
		case 0x16:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mmu->isAuxZeropage());
			break;
		case 0x17:
			if (isIIcIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									mouse->clearYInterrupt());
			else if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									isSlot3External());
			break;
		case 0x18:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->is80Store());
			break;
		case 0x19:
			if (isIIcIOU)
				return maskWithBit7(keyboard->getKeyLatch(), 
					timingGenerator->isVblInterruptAsserted());
			else if (isIIGSIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									timingGenerator->isVbl());
			else if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									!timingGenerator->isVbl());
			break;
		case 0x1a:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->isText());
			break;
		case 0x1b:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->isMixed());
			break;
		case 0x1c:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->isPage2());
			break;
		case 0x1d:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->isHires());
			break;
		case 0x1e:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->isAltChar());
			break;
		case 0x1f:
			if (isIIeIOU)
				return maskWithBit7(keyboard->getKeyLatch(),
									videoRenderer->is80Col());
			break;
			
		case 0x20:
			speaker->toggleCassetteOutput();
			break;
		case 0x21:
			if (isIIGSIOU)
				return videoRenderer->getMonochromeRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x22:
			if (isIIGSIOU)
				return videoRenderer->getScreenColorRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x23:
			if (isIIGSIOU)
				return timingGenerator->getVgcInterruptRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x24:
			if (isIIGSIOU)
				adb->getMouseDataRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x25:
			if (isIIGSIOU)
				return adb->getKeyModifierRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x26:
			if (isIIGSIOU)
				return adb->getDataRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x27:
			if (isIIGSIOU)
				return adb->getStatusRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x28:
			if (isIIcIOU || isIIGSIOU)
				mmu->setRomBank2(!mmu->isRomBank2());
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x29:
			if (isIIGSIOU)
				return videoRenderer->getNewVideoRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2a:
			if (!isIIGSIOU)
				speaker->toggleCassetteOutput();
			break;
		case 0x2b:
			if (isIIGSIOU)
				return videoRenderer->getLanguageRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2c:
			if (isIIGSIOU)
				return videoRenderer->getCharacterSetReadAddress();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2d:
			if (isIIGSIOU)
				return getSlotRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2e:
			if (isIIGSIOU)
				timingGenerator->getVerticalCountRegister();
			else
				speaker->toggleCassetteOutput();
			break;
		case 0x2f:
			if (isIIGSIOU)
				timingGenerator->getHorizontalCountRegister();
			else
				speaker->toggleCassetteOutput();
			break;
			
		case 0x30:
			speaker->toggleSpeakerOutput();
			break;
		case 0x31:
			if (isIIGSIOU)
				return slot[SLOT_INTERNAL + 6]->readIo(address);
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x32:
			if (!isIIGSIOU)
				speaker->toggleSpeakerOutput();
			break;
		case 0x33:
			if (isIIGSIOU)
				return rtc->receiveData();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x34:
			if (isIIGSIOU)
				videoRenderer->getBorderColorRegister();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x35:
			if (isIIGSIOU)
				return mmu->getShadowRegister();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x36:
			if (isIIGSIOU)
				return timingGenerator->getSpeedRegister();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x37:
			if (isIIGSIOU)
				return mmu->getDmaBank();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x38:
			if (isIIGSIOU)
				return sccB->getCommand();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x39:
			if (isIIGSIOU)
				return sccA->getCommand();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3a:
			if (isIIGSIOU)
				return sccB->receiveData();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3b:
			if (isIIGSIOU)
				return sccA->receiveData();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3c:
			if (isIIGSIOU)
				return doc->getControlRegister();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3d:
			if (isIIGSIOU)
				return doc->receiveData();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3e:
			if (isIIGSIOU)
				return doc->getAddressLow();
			else
				speaker->toggleSpeakerOutput();
			break;
		case 0x3f:
			if (isIIGSIOU)
				return doc->getAddressHigh();
			else
				speaker->toggleSpeakerOutput();
			break;
			
		case 0x40:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									mouse->isXYInterruptsEnabled());
			break;
		case 0x41:
			if (isIIGSIOU)
				return mouse->getInterruptRegister();
			else if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									timingGenerator->isVblInterruptEnabled());
			break;
		case 0x42:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									mouse->isXInterruptOnFallingEdge());
			break;
		case 0x43:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									mouse->isYInterruptOnFallingEdge());
			break;
		case 0x44:
			if (isIIGSIOU)
				return mouse->getDeltaXRegister();
			break;
		case 0x45:
			if (isIIGSIOU)
				return mouse->getDeltaYRegister();
			break;
		case 0x46:
			if (isIIGSIOU) {
				UINT8 interruptFlagRegister = mouse->getInterruptFlagRegister();
				setBit(interruptFlagRegister, INTERRUPTFLAG_IRQASSERTED,
					   cpu->isIrqAsserted());
				setBit(interruptFlagRegister, INTERRUPTFLAG_VBLINTASSERTED,
					   timingGenerator->isVblInterruptAsserted());
				setBit(interruptFlagRegister,
					   INTERRUPTFLAG_QUARTERSECINTASSERTED,
					   timingGenerator->isQuarterSecondInterruptAsserted());
				setBit(interruptFlagRegister, INTERRUPTFLAG_AN3,
					   paddles->getAnnunciator(3));
				return interruptFlagRegister;
			}
			break;
		case 0x47:
			break;
		case 0x48:
			if (isIIcIOU) {
				mouse->clearXInterrupt();
				mouse->clearYInterrupt();
			}
			break;
		case 0x49: case 0x4a: case 0x4b:
		case 0x4c: case 0x4d: case 0x4e: case 0x4f:
			break;
			
		case 0x50: case 0x51:
			videoRenderer->setText(address & 0x1);
			break;
		case 0x52: case 0x53:
			videoRenderer->setMixed(address & 0x1);
			break;
		case 0x54: case 0x55:
			videoRenderer->setPage2(address & 0x1);
			if (isIIeIOU)
				mmu->setPage2(address & 0x1);
			break;
		case 0x56: case 0x57:
			videoRenderer->setHires(address & 0x1);
			if (isIIeIOU)
				mmu->setHires(address & 0x1);
			break;
		case 0x58: case 0x59:
			if (isIIcIOUEN)
				mouse->enableXYInterrupts(address & 0x1);
			else
				paddles->setAnnunciator(0, address & 0x1);
			if (isAn0ConnectedToVideoterm)
				videoRenderer->setVideoterm(address & 0x1);
			return chip->readIo(address);
		case 0x5a: case 0x5b:
			if (isIIcIOUEN)
				timingGenerator->enableVblInterrupt(address & 0x1);
			else
				paddles->setAnnunciator(1, address & 0x1);
			return chip->readIo(address);
		case 0x5c: case 0x5d:
			if (isIIcIOUEN)
				mouse->setXInterruptOnFallingEdge(address & 0x1);
			else {
				if (isIIjplusIOU)
					videoRenderer->setAltChar(address & 0x1);
				paddles->setAnnunciator(2, address & 0x1);
			}
			return chip->readIo(address);
		case 0x5e: case 0x5f:
			if (isIIcIOUEN)
				mouse->setYInterruptOnFallingEdge(address & 0x1);
			else {
				if (isIIeEnhancedIOU)
					videoRenderer->setDHires(!(address & 0x1));
				paddles->setAnnunciator(3, address & 0x1);
			}
			return chip->readIo(address);
			
		case 0x68:
			if (isIIGSIOU)
				return mmu->getStateRegister();
		case 0x60:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									keyboard->isIIc40Switch());
			else
				return maskWithBit7(timingGenerator->readFloatingBus(),
									speaker->isCassetteInputHigh());
		case 0x69:
		case 0x61:
			return maskWithBit7(timingGenerator->readFloatingBus(),
								paddles->isButtonDown(0));
		case 0x6a:
		case 0x62:
			return maskWithBit7(timingGenerator->readFloatingBus(),
								paddles->isButtonDown(1));
		case 0x6b:
		case 0x63:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									!mouse->isButtonDown(0));
			return maskWithBit7(timingGenerator->readFloatingBus(),
								paddles->isButtonDown(2));
		case 0x6c:
		case 0x64:
			return maskWithBit7(timingGenerator->readFloatingBus(),
								paddles->isTimedOut(0));
		case 0x6d:
		case 0x65:
			return maskWithBit7(timingGenerator->readFloatingBus(),
								paddles->isTimedOut(1));
		case 0x6e:
		case 0x66:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									mouse->isXDirectionHigh());
			else
				return maskWithBit7(timingGenerator->readFloatingBus(),
									paddles->isTimedOut(2));
		case 0x6f:
		case 0x67:
			if (isIIcIOU)
				return maskWithBit7(timingGenerator->readFloatingBus(),
									mouse->isYDirectionHigh());
			else
				return maskWithBit7(timingGenerator->readFloatingBus(),
									paddles->isTimedOut(3));
			
		case 0x70: case 0x71: case 0x72: case 0x73:
		case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b:
		case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			paddles->resetTimer();
			if (isIIcIOU) {
				timingGenerator->clearVblInterrupt();
				if ((address & 0xf) == 0xe)
					return maskWithBit7(timingGenerator->readFloatingBus(),
										!isIIcIOUEN);
				if ((address & 0xf) == 0xf)
					return maskWithBit7(timingGenerator->readFloatingBus(),
										!videoRenderer->isDHires());
			}
			if (isIIGSIOU)
				return mmu->readMemoryDirect(address);
			return chip->readIo(address, auxSlot->readIo(address));
		case 0x80: case 0x81: case 0x82: case 0x83:
		case 0x84: case 0x85: case 0x86: case 0x87:
		case 0x88: case 0x89: case 0x8a: case 0x8b:
		case 0x8c: case 0x8d: case 0x8e: case 0x8f:
			return mappedSlot[0]->readIo(address);
		case 0x90: case 0x91: case 0x92: case 0x93:
		case 0x94: case 0x95: case 0x96: case 0x97:
		case 0x98: case 0x99: case 0x9a: case 0x9b:
		case 0x9c: case 0x9d: case 0x9e: case 0x9f:
			return mappedSlot[1]->readIo(address);
		case 0xa0: case 0xa1: case 0xa2: case 0xa3:
		case 0xa4: case 0xa5: case 0xa6: case 0xa7:
		case 0xa8: case 0xa9: case 0xaa: case 0xab:
		case 0xac: case 0xad: case 0xae: case 0xaf:
			return mappedSlot[2]->readIo(address);
		case 0xb0: case 0xb1: case 0xb2: case 0xb3:
		case 0xb4: case 0xb5: case 0xb6: case 0xb7:
		case 0xb8: case 0xb9: case 0xba: case 0xbb:
		case 0xbc: case 0xbd: case 0xbe: case 0xbf:
			return slot[3]->readIo(address);
		case 0xc0: case 0xc1: case 0xc2: case 0xc3:
		case 0xc4: case 0xc5: case 0xc6: case 0xc7:
		case 0xc8: case 0xc9: case 0xca: case 0xcb:
		case 0xcc: case 0xcd: case 0xce: case 0xcf:
			return mappedSlot[4]->readIo(address);
		case 0xd0: case 0xd1: case 0xd2: case 0xd3:
		case 0xd4: case 0xd5: case 0xd6: case 0xd7:
		case 0xd8: case 0xd9: case 0xda: case 0xdb:
		case 0xdc: case 0xdd: case 0xde: case 0xdf:
			return mappedSlot[5]->readIo(address);
		case 0xe0: case 0xe1: case 0xe2: case 0xe3:
		case 0xe4: case 0xe5: case 0xe6: case 0xe7:
		case 0xe8: case 0xe9: case 0xea: case 0xeb:
		case 0xec: case 0xed: case 0xee: case 0xef:
			return mappedSlot[6]->readIo(address);
		case 0xf0: case 0xf1: case 0xf2: case 0xf3:
		case 0xf4: case 0xf5: case 0xf6: case 0xf7:
		case 0xf8: case 0xf9: case 0xfa: case 0xfb:
		case 0xfc: case 0xfd: case 0xfe: case 0xff:
			return mappedSlot[7]->readIo(address);
		default:
			if (address & 0x800) {
				if (isIIcIOU)
					return mig->readIo(address);
				else if ((address & 0xfff) == 0xfff)
					ioSlotSel = 0;
				else
					return mappedSlot[ioSlotSel]->readMemory(address);
			} else {
				ioSlotSel = (address >> 8) & 0x7;
				return mappedSlot[ioSlotSel]->readMemory(address);
			}
	}
	
	return timingGenerator->readFloatingBus();
}

void IOU::setSlot3External(bool value) {
	isSlot3ExternalValue = value;
	mappedSlot[3] = value ?
		slot[SLOT_EXTERNAL + 3] :
		slot[SLOT_INTERNAL + 3];
}

bool IOU::isSlot3External() {
	return isSlot3ExternalValue;
}

void IOU::setSlotRegister(UINT8 slotRegister) {
	this->slotRegister = slotRegister;
	for (int index = 0; index < MAPPEDSLOT_NUM; index++) {
		if ((index == 0) | (index == 3))
			continue;
		mappedSlot[index] = getBit(slotRegister, index) ?
			slot[SLOT_EXTERNAL + index] :
			slot[SLOT_INTERNAL + index];
	}
}

UINT8 IOU::getSlotRegister() {
	return slotRegister;
}

void IOU::connectAn0ToVideoterm(bool value) {
	isAn0ConnectedToVideoterm = value;
}
